//-------------------------------------------------------------------
//	Copyright (c) 2012-2014 Zhirnov Andrey
//	This file is part of the "UXGEN-ES" project.
//	See copyright notice in "Engine.h".
//-------------------------------------------------------------------

#pragma once

#include "Platforms/Android/APlatform.h"

#if defined( PLATFORM_ANDROID )

#include "Platforms/Android/AUtils.h"

UX_DECL_JAVA_TYPE( Google_LicenseChecker,			"com/google/android/vending/licensing/LicenseChecker" )
UX_DECL_JAVA_TYPE( Google_LicenseCheckerCallback,	"com/google/android/vending/licensing/LicenseCheckerCallback" )
UX_DECL_JAVA_TYPE( Google_Policy,					"com/google/android/vending/licensing/Policy" )
UX_DECL_JAVA_TYPE( Google_ServerManagedPolicy,		"com/google/android/vending/licensing/ServerManagedPolicy" )
UX_DECL_JAVA_TYPE( Google_AESObfuscator,			"com/google/android/vending/licensing/AESObfuscator" )
UX_DECL_JAVA_TYPE( Google_Obfuscator,				"com/google/android/vending/licensing/Obfuscator" )
UX_DECL_JAVA_TYPE( Android_Context,					"android/content/Context" )
//UX_DECL_JAVA_TYPE( UXGENES_NativeLib,				"uxgenes/core/NativeLib" )

namespace Engine
{
namespace Platforms
{
	
	//
	// Android Java Interface
	//

	class AndroidJavaInterface
	{
	// types
	public:
		struct JavaEnvSetter
		{
			JavaEnvSetter (android::JNIEnv *env, android::jobject instance)
			{
				AndroidPlatform::GetInstance()->GetJavaInterface()->_SetEnv( env, instance );
			}

			~JavaEnvSetter ()
			{
				AndroidPlatform::GetInstance()->GetJavaInterface()->_SetEnv( null, null );
			}
		};


	// variables
	private:
		Java::JavaObject				_interface;

		AndroidPlatform &				_platform;
		Ptr< android::AAssetManager >	_assetManager;

		IPlatform::OnLicenseChecked_t	_licenseCheckedCallback;


	// methods
	public:
		AndroidJavaInterface (AndroidPlatform &platform);
		~AndroidJavaInterface ();

		void SetAssetManager (android::jobject am);

		// Java Functions //
		void CloseActivity ();

		void Notification (const string &text, bool longTime);
		void Message (int id, const string &caption, const string &text, const string &positive, const string &negative);
		void OpenURL (const string &url);

		void SendEmail (const string &address, const string &subject, const string &text, const string &attachment);

		void BackgroundMode (bool enabled);
		void EnableAudio (bool enabled);
		void Vibrate (const uni_array<const int64> &pattern);
		void SetSensorState (ESensor::type sensor, bool enabled, int delay);

		void ProcessEngineEvent (uint type, uint param0, uint param1, uint param2);
		void SetDisplayOrientation (EDisplayOrientation::type orient);
		
		void CheckLicense (IPlatform::OnLicenseChecked_t callback, const string &publicKey, const string &deviceId);
		void OnCheckLicenseResponse (ELicenseCheckResponse::type code, const string &error);
		
		bool IsNetworkConnected ();

		Ptr< android::AAssetManager >	GetAssetManager ()		{ return _assetManager; }
		static Ptr< AndroidHwCamera >	GetCamera ()			{ return AndroidPlatform::GetInstance()->GetCamera().To< Ptr< AndroidHwCamera > >(); }

		// Extensions
		void EnableExtension (int ext, bool enable);
		void CameraCommand (int cmd, float arg0, float arg1, float arg2);

	private:
		void _SetEnv (android::JNIEnv *env, android::jobject instance);
	};
	

	
//----------------------- AndroidJavaInterface --------------------//
/*
=================================================
	constructor
=================================================
*/
	AndroidJavaInterface::AndroidJavaInterface (AndroidPlatform &platform) :
		_platform(platform), _interface(null)
	{
	}
	
/*
=================================================
	destructor
=================================================
*/
	AndroidJavaInterface::~AndroidJavaInterface ()
	{
	}
	
/*
=================================================
	_SetEnv
=================================================
*/
	void AndroidJavaInterface::_SetEnv (android::JNIEnv *env, android::jobject instance)
	{
		using namespace Java;

		_interface = JavaObject( instance );
	}
	
/*
=================================================
	SetAssetManager
=================================================
*/
	void AndroidJavaInterface::SetAssetManager (android::jobject am)
	{
		Java::JavaEnv je;
		_assetManager = AAssetManager_fromJava( *je, am );
	}

/*
=================================================
	CloseActivity
=================================================
*/
	void AndroidJavaInterface::CloseActivity ()
	{
		// call void NativeLib.CloseActivity ();
		using namespace Java;
		LOG( "CloseActivity", ELog::DEBUG );
		
		JavaMethod< void () >		method( _interface, "CloseActivity" ); 
		
		method.Call();
	}
	
/*
=================================================
	Notification
=================================================
*/
	void AndroidJavaInterface::Notification (const string &text, bool longTime)
	{
		// call void NativeLib.Notification (String text, boolean longTime);
		using namespace Java;
		LOG( "Notification", ELog::DEBUG );

		JavaMethod< void (jstring, jboolean) >		method( _interface, "Notification" );
		
		UTFString		j_text( text );
		
		method.Call( j_text, longTime );
	}

/*
=================================================
	Message
=================================================
*/
	void AndroidJavaInterface::Message (int id, const string &caption, const string &text,
										const string &positive, const string &negative)
	{
		// call void NativeLib.Message (int id, String caption, String text, String positive, String negative);
		using namespace Java;
		LOG( "Message", ELog::DEBUG );

		JavaMethod< void (jint, jstring, jstring, jstring, jstring) >	method( _interface, "Message" );

		UTFString		j_caption( caption ),
						j_text( text ),
						j_positive( positive ),
						j_negative( negative );

		method.Call( id, j_caption, j_text, j_positive, j_negative );
	}
	
/*
=================================================
	OpenURL
=================================================
*/
	void AndroidJavaInterface::OpenURL (const string &url)
	{
		// call void NativeLib.OpenURL (String url);
		using namespace Java;
		LOG( "OpenURL", ELog::DEBUG );

		JavaMethod< void (jstring) >	method( _interface, "OpenURL" );

		UTFString		j_url( url );

		method.Call( j_url );
	}

/*
=================================================
	SendEmail
=================================================
*/
	void AndroidJavaInterface::SendEmail (const string &address, const string &subject,
										  const string &text, const string &attachment)
	{
		// call void NativeLib.SendEmail (String addresses, String subject, String text, String attachment)
		using namespace Java;
		LOG( "SendEmail", ELog::DEBUG );
		
		JavaMethod< void (jstring, jstring, jstring, jstring) >	method( _interface, "SendEmail" );
		
		UTFString		j_address( address ),
						j_subject( subject ),
						j_text( text ),
						j_attachment( attachment );

		method.Call( j_address, j_subject, j_text, j_attachment );
	}

/*
=================================================
	BackgroundMode
=================================================
*/
	void AndroidJavaInterface::BackgroundMode (bool enabled)
	{
		// call void NativeLib.BackgroundMode (boolean enabled)
		using namespace Java;
		LOG( "BackgroundMode", ELog::DEBUG );
		
		JavaMethod< void (jboolean) >	method( _interface, "BackgroundMode" );

		method.Call( enabled );
	}
	
/*
=================================================
	EnableAudio
=================================================
*/
	void AndroidJavaInterface::EnableAudio (bool enabled)
	{
		// call void NativeLib.EnableAudioFocus (boolean enabled)
		using namespace Java;
		LOG( "EnableAudioFocus", ELog::DEBUG );
		
		JavaMethod< void (jboolean) >	method( _interface, "EnableAudioFocus" );

		method.Call( enabled );
	}
	
/*
=================================================
	Vibrate
=================================================
*/
	void AndroidJavaInterface::Vibrate (const uni_array<const int64> &pattern)
	{
		// call void NativeLib.Vibrate (long[] pattern)
		using namespace Java;
		LOG( "Vibrate", ELog::DEBUG );
		
		JavaMethod< void (jlongArray) >	method( _interface, "Vibrate" );

		JavaArray< jlong >	arr( pattern );

		method.Call( arr );
	}

/*
=================================================
	SetSensorState
=================================================
*/
	void AndroidJavaInterface::SetSensorState (ESensor::type sensor, bool enabled, int delay)
	{
		// call void NativeLib.SetSensorState (int sensor, boolean enabled, int delay)
		using namespace Java;
		LOG( "SetSensorState", ELog::DEBUG );
		
		JavaMethod< void (jint, jboolean, jint) >	method( _interface, "SetSensorState" );

		method.Call( (int)sensor, enabled, delay );
	}
	
/*
=================================================
	EnableExtension
=================================================
*/
	void AndroidJavaInterface::EnableExtension (int ext, bool enable)
	{
		// call void NativeLib.EnableExtension (int ext, boolean enable)
		using namespace Java;
		LOG( "EnableExtension", ELog::DEBUG );

		JavaMethod< void (jint, jboolean) >	method( _interface, "EnableExtension" );

		method.Call( ext, enable );
	}
	
/*
=================================================
	CameraCommand
=================================================
*/
	void AndroidJavaInterface::CameraCommand (int cmd, float arg0, float arg1, float arg2)
	{
		// call void NativeLib.CameraCommand (int cmd, float arg0, float arg1, float arg2)
		using namespace Java;
		LOG( "CameraCommand", ELog::DEBUG );

		JavaMethod< void (jint, jfloat, jfloat, jfloat) >	method( _interface, "CameraCommand" );

		method.Call( cmd, arg0, arg1, arg2 );
	}
	
/*
=================================================
	ProcessEngineEvent
=================================================
*/
	void AndroidJavaInterface::ProcessEngineEvent (uint type, uint param0, uint param1, uint param2)
	{
		// call void NativeLib.ProcessEngineEvent (int event, int param0, int param1, int param2)
		using namespace Java;
		LOG( "ProcessEngineEvent", ELog::DEBUG );

		JavaMethod< void (jint, jint, jint, jint) >	method( _interface, "ProcessEngineEvent" );

		method.Call( type, param0, param1, param2 );
	}
	
/*
=================================================
	SetDisplayOrientation
=================================================
*/
	void AndroidJavaInterface::SetDisplayOrientation (EDisplayOrientation::type orient)
	{
		// call void NativeLib.SetDisplayOrientation (int orient)
		using namespace Java;
		LOG( "SetDisplayOrientation", ELog::DEBUG );

		JavaMethod< void (jint) >	method( _interface, "SetDisplayOrientation" );

		method.Call( (int)orient );
	}
	
/*
=================================================
	CheckLicense
=================================================
*/
	void AndroidJavaInterface::CheckLicense (IPlatform::OnLicenseChecked_t callback, const string &publicKey, const string &deviceId)
	{
		using namespace Java;

		JavaEnv	je;

		OnCheckLicenseResponse( ELicenseCheckResponse::DETACHED_LISTENER, "" );

		_licenseCheckedCallback = callback;
		
		JavaObject	license_checker;
		JavaObject	server_manager_policy;
		JavaObject	aes_obfuscator;

		JavaClass	license_checker_class		= JavaClass::FromType< Google_LicenseChecker >();
		JavaClass	server_managed_policy_class	= JavaClass::FromType< Google_ServerManagedPolicy >();
		JavaClass	aes_obfuscator_class		= JavaClass::FromType< Google_AESObfuscator >();
		
		UTFString	j_public_key( publicKey ),
					j_device_id( deviceId );

		static_array< jbyte, 20 >	salt_array;

		FOR( i, salt_array ) {
			salt_array[i] = Random::Int<jbyte>();
		}

		JavaArray< jbyte >		j_salt_array( (uni_array<jbyte>) salt_array );

		// context = nativeLib.GetContext()
		JavaMethod< Android_Context () >	native_lib_get_context( _interface, "GetContext" );
		JavaObject	android_context( native_lib_get_context.Call() );

		// packageName = context.getPackageName()
		JavaMethod< jstring () >	context_get_package_name( android_context, "getPackageName" );
		jstring	package_name = context_get_package_name.Call();
		
		// aesObfuscator = new AESObfuscator( salt, package, deviceId )
		JavaObjectConstructor< void (jbyteArray, jstring, jstring) >	aes_obfuscator_ctor( aes_obfuscator_class );
		aes_obfuscator = aes_obfuscator_ctor.Call( j_salt_array, package_name, j_device_id );
		
		// serverManagerPolicy = new ServerManagerPolicy( context, aesObfuscator )
		JavaObjectConstructor< void (Android_Context, Google_Obfuscator) >	server_managed_policy_ctor( server_managed_policy_class );
		server_manager_policy = server_managed_policy_ctor.Call( android_context, aes_obfuscator );
		
		// licenseChacker = new LicenseChecker( context, serverManagerPolicy, publicKey )
		JavaObjectConstructor< void (Android_Context, Google_Policy, jstring) >	license_checker_ctor( license_checker_class );
		license_checker = license_checker_ctor.Call( android_context, server_manager_policy, j_public_key );

		// licenseChecker.checkAccess( nativeLib )
		JavaMethod< void (Google_LicenseCheckerCallback) >		license_checker_check_access( license_checker, "checkAccess" );
		license_checker_check_access.Call( _interface );
	}
	
/*
=================================================
	OnCheckLicenseResponse
=================================================
*/
	void AndroidJavaInterface::OnCheckLicenseResponse (ELicenseCheckResponse::type code, const string &error)
	{
		if ( _licenseCheckedCallback.IsValid() )
		{
			AndroidPlatform::GetInstance()->Push( ParallelOp::FunctionCall( FunctionBuilder::Create(
				&AndroidPlatform::OnLicenseChecked, _licenseCheckedCallback, code, error ) ) );
		}

		_licenseCheckedCallback = IPlatform::OnLicenseChecked_t();
	}
	
/*
=================================================
	IsNetworkConnected
=================================================
*/
	bool AndroidJavaInterface::IsNetworkConnected ()
	{
		// call boolean NativeLib.IsNetworkConnected ()
		using namespace Java;
		LOG( "IsNetworkConnected", ELog::DEBUG );

		JavaMethod< jboolean () >	method( _interface, "IsNetworkConnected" );

		return method.Call();
	}
	
//-----------------------------------------------------------------//


	using namespace android;
	typedef UXTypes::uint	uint;

#	define JAVA_ENV()			AndroidJavaInterface::JavaEnvSetter	__javaEnvSetter( env, obj )
#	define APLATFORM()			AndroidPlatform::GetInstance()
#	define AJAVAINTERFACE()		APLATFORM()->GetJavaInterface()

	

//------------------------- Native Fucntions ----------------------//
/*
=================================================
	OnInit
=================================================
*/
	NATIVE( void, OnInit ) (JNI_ARGS, jobject assetManager, jint supportedSensors)
	{
		AndroidPlatform::CreateInstance();

		JAVA_ENV();
		APLATFORM()->Init( supportedSensors );
		AJAVAINTERFACE()->SetAssetManager( assetManager );
	}
	
/*
=================================================
	OnQuit
=================================================
*/
	NATIVE( void, OnQuit ) (JNI_ARGS)
	{
		AndroidPlatform::DestroyInstance();
	}

/*
=================================================
	OnStart
=================================================
*/
	NATIVE( void, OnStart ) (JNI_ARGS)
	{
		JAVA_ENV();
		APLATFORM()->EnterForeground( false );
	}
	
/*
=================================================
	OnStop
=================================================
*/
	NATIVE( void, OnStop ) (JNI_ARGS)
	{
		JAVA_ENV();
		APLATFORM()->EnterBackground( true );
	}
	
/*
=================================================
	OnPause
=================================================
*/
	NATIVE( void, OnPause ) (JNI_ARGS)
	{
		JAVA_ENV();
		APLATFORM()->EnterBackground( false );
	}
	
/*
=================================================
	OnResume
=================================================
*/
	NATIVE( void, OnResume ) (JNI_ARGS)
	{
		JAVA_ENV();
		APLATFORM()->EnterForeground( true );
	}
	
/*
=================================================
	OnSurfaceChanged
=================================================
*/
	NATIVE( void, OnSurfaceChanged ) (JNI_ARGS, jobject surface, jint width, jint height)
	{
		JAVA_ENV();

		APLATFORM()->SurfaceChanged( AndroidWindowSurface( env, surface ), width, height );
	}
	
/*
=================================================
	OnSurfaceChanged
=================================================
*/
	NATIVE( void, OnSurfaceDestroyed ) (JNI_ARGS)
	{
		if ( APLATFORM().IsNull() )
			return;

		JAVA_ENV();
		APLATFORM()->SurfaceDestroyed();
	}
	
/*
=================================================
	VisibilityChanged
=================================================
*/
	NATIVE( void, VisibilityChanged ) (JNI_ARGS, jboolean isVisible)
	{
		JAVA_ENV();
		APLATFORM()->VisibilityChanged( isVisible );
	}

/*
=================================================
	ProcessMessages
=================================================
*/
	NATIVE( jint, ProcessMessages ) (JNI_ARGS)
	{
		if ( APLATFORM().IsNull() )
			return 0;

		JAVA_ENV();
		APLATFORM()->Flush();
		return APLATFORM()->ProcessMessages();
	}
	
/*
=================================================
	LowMemory
=================================================
*/
	NATIVE( void, LowMemory ) (JNI_ARGS)
	{
		JAVA_ENV();
		APLATFORM()->LowMemory();
	}

/*
=================================================
	MessageResult
=================================================
*/
	NATIVE( void, MessageResult ) (JNI_ARGS, jint messageID, jint result)
	{
		JAVA_ENV();
		APLATFORM()->MessageResult( messageID, result );
	}

/*
=================================================
	SetDirectory
=================================================
*/
	NATIVE( void, SetDirectory ) (JNI_ARGS, jstring internal, jstring internalCache,
								  jstring external, jstring externalCache, jstring externalStorage)
	{
		using namespace Java;

		JAVA_ENV();

		APLATFORM()->SetDirectory( UTFString( internal ),			EDirectory::INTERNAL		 );
		APLATFORM()->SetDirectory( UTFString( internalCache ),		EDirectory::INTERNAL_CACHE	 );
		APLATFORM()->SetDirectory( UTFString( external ),			EDirectory::EXTERNAL		 );
		APLATFORM()->SetDirectory( UTFString( externalCache ),		EDirectory::EXTERNAL_CACHE	 );
		APLATFORM()->SetDirectory( UTFString( externalStorage ),	EDirectory::EXTERNAL_STORAGE );
	}
	
/*
=================================================
	SetDeviceInfo
=================================================
*/
	NATIVE( void, SetDeviceInfo ) (	JNI_ARGS,
									jstring name, jstring os, jstring deviceID, jstring package, jstring locale,
									jint numCPUs, jint cpuFreq, jlong totalMem, jint apiVersion,
									jint width, jint height, jint ppi, jint orientation )
	{
		JAVA_ENV();
		using namespace Java;

		enum {
			ROTATION_0	 = 0x00,
			ROTATION_90	 = 0x01,
			ROTATION_180 = 0x02,
			ROTATION_270 = 0x03,
		};

		UTFString		name_str( name );
		UTFString		os_str( os );
		UTFString		dev_str( deviceID );
		UTFString		loc_str( locale );
		UTFString		package_str( package );

		DisplayWrap		disp;
		DeviceWrap *	dev = new DeviceWrap();

		Display::EOrientation	orient = Display::ORIENT_0;

		switch ( orientation )
		{
			case ROTATION_0   :		orient = Display::ORIENT_0;		break;
			case ROTATION_90  :		orient = Display::ORIENT_90;	break;
			case ROTATION_180 :		orient = Display::ORIENT_180;	break;
			case ROTATION_270 :		orient = Display::ORIENT_270;	break;
		}

		disp.SetOrientation( orient );
		disp.SetResolution( ivec2( width, height ) );
		disp.SetPPI( ppi );
		disp.SetPhysicalSize( disp.CalcPhysicsSize().Get() );

		dev->SetDisplay( disp );
		dev->SetDeviceName( name_str );
		dev->SetOSName( os_str );
		dev->SetLocale( loc_str );
		dev->SetCpuFreq( cpuFreq );
		dev->SetTotalRAM( totalMem );
		dev->SetNumCPUs( numCPUs );
		dev->SetDeviceID( dev_str );
		dev->SetPackage( package_str );

		APLATFORM()->SetDevice( dev );
	}
	
/*
=================================================
	OnDisplayOrientationChanged
=================================================
*/
	NATIVE( void, OnDisplayOrientationChanged ) ( JNI_ARGS, jint orientation)
	{
		JAVA_ENV();

		enum {
			ROTATION_0	 = 0x00,
			ROTATION_90	 = 0x01,
			ROTATION_180 = 0x02,
			ROTATION_270 = 0x03,
		};

		Display::EOrientation	orient = Display::ORIENT_0;

		switch ( orientation )
		{
			case ROTATION_0   :		orient = Display::ORIENT_0;		break;
			case ROTATION_90  :		orient = Display::ORIENT_90;	break;
			case ROTATION_180 :		orient = Display::ORIENT_180;	break;
			case ROTATION_270 :		orient = Display::ORIENT_270;	break;
		}

		APLATFORM()->UpdateDisplayOrientation( orient );
	}
	
/*
=================================================
	OnTouch
=================================================
*/
	NATIVE( void, OnTouch ) (JNI_ARGS, jint action, jint count, jfloatArray data)
	{
		JAVA_ENV();
		using namespace Java;

		enum {
			ACTION_MOTION	= 1,
			ACTION_DOWN		= 2,
			ACTION_UP		= 4,
		};

		const JavaArray< float >	touches_data( data, true );

		if ( action == ACTION_MOTION )
		{
			int	j = 0;

			for (int i = 0; i < count; ++i)
			{
				APLATFORM()->OnTouchMotion( (int) touches_data[j++], touches_data[j++],
											touches_data[j++], touches_data[j++] );
			}
		}
		else
		{
			APLATFORM()->OnTouch( action == ACTION_DOWN, (int) touches_data[0],
									touches_data[1], touches_data[2], touches_data[3] );
		}
	}
	
/*
=================================================
	OnTouch
=================================================
*/
	NATIVE( void, OnKey ) (JNI_ARGS, jint keyCode, jboolean down)
	{
		JAVA_ENV();
		APLATFORM()->OnKey( keyCode, down );
	}
	
/*
=================================================
	OnAcceleration
=================================================
*/
	NATIVE( void, OnAcceleration ) (JNI_ARGS, jfloat x, jfloat y, jfloat z)
	{
		JAVA_ENV();
		APLATFORM()->OnAcceleration( x, y, z );
	}

/*
=================================================
	OnMagneticField
=================================================
*/
	NATIVE( void, OnMagneticField ) (JNI_ARGS, jfloat x, jfloat y, jfloat z)
	{
		JAVA_ENV();
		APLATFORM()->OnMagneticField( x, y, z );
	}
	
/*
=================================================
	OnGyroscope
=================================================
*/
	NATIVE( void, OnGyroscope ) (JNI_ARGS, jfloat x, jfloat y, jfloat z)
	{
		JAVA_ENV();
		APLATFORM()->OnGyroscope( x, y, z );
	}
	
/*
=================================================
	OnOrientation
=================================================
*/
	NATIVE( void, OnOrientation ) (JNI_ARGS, jfloat x, jfloat y, jfloat z)
	{
		JAVA_ENV();
		APLATFORM()->OnOrientation( x, y, z );
	}
	
/*
=================================================
	OnRotation
=================================================
*/
	NATIVE( void, OnRotation ) (JNI_ARGS, jfloat x, jfloat y, jfloat z)
	{
		JAVA_ENV();
		APLATFORM()->OnRotation( x, y, z );
	}
	
/*
=================================================
	OnLightSensor
=================================================
*/
	NATIVE( void, OnLightSensor ) (JNI_ARGS, jfloat level)
	{
		JAVA_ENV();
		APLATFORM()->OnLightSensor( level );
	}
	
/*
=================================================
	OnProximitySensor
=================================================
*/
	NATIVE( void, OnProximitySensor ) (JNI_ARGS, jfloat distance)
	{
		JAVA_ENV();
		APLATFORM()->OnProximitySensor( distance );
	}
	
/*
=================================================
	SetLocationStatus
=================================================
*/
	NATIVE( void, SetLocationStatus ) (JNI_ARGS, jint state)
	{
		JAVA_ENV();

		enum
		{
			 OUT_OF_SERVICE				= 0,
			 TEMPORARILY_UNAVAILABLE	= 1,
			 AVAILABLE					= 2,
		};

		SysEvent::LocationStatus::EType	type = SysEvent::LocationStatus::NONE;

		switch ( state )
		{
			case TEMPORARILY_UNAVAILABLE :
			case OUT_OF_SERVICE :			type = SysEvent::LocationStatus::DISABLED;	break;
			case AVAILABLE :				type = SysEvent::LocationStatus::ENABLED;	break;
		}

		APLATFORM()->SetLocationStatus( type );
	}

/*
=================================================
	OnLocation
=================================================
*/
	NATIVE( void, OnLocation ) (JNI_ARGS, jlong time, jdouble lon, jdouble lat, jfloat alt, jfloat accur, jfloat speed)
	{
		JAVA_ENV();
		APLATFORM()->OnLocation( time, lon, lat, alt, accur, speed );
	}

/*
=================================================
	AudioFocusChanged
=================================================
*/
	NATIVE( void, AudioFocusChanged ) (JNI_ARGS, jint state)
	{
		JAVA_ENV();

		SysEvent::Audio::EType	type = SysEvent::Audio::NONE;

		switch ( state )
		{
			case 1 :	type = SysEvent::Audio::SET_FOCUS;	break;
			case 2 :	type = SysEvent::Audio::KILL_FOCUS;	break;
			case 2|4 :	type = SysEvent::Audio::PAUSE;		break;
			default :	WARNING( "unknown focus state" );
		}

		APLATFORM()->AudioFocusChanged( type );
	}
	
/*
=================================================
	CameraPreview
=================================================
*/
	NATIVE( void, CameraPreview ) (JNI_ARGS, jbyteArray data, jint width, jint height, jint format)
	{
		JAVA_ENV();
		using namespace Java;

		Ptr< AndroidHwCamera >	cam = AJAVAINTERFACE()->GetCamera();

		if ( cam.IsNull() )
			return;

		JavaArray< jbyte >	jni_data( data, true );

		cam->OnPreview( jni_data.To<uint8>(), ivec2( width, height ), HardwareCamera::ImageFormat::type(format) );
	}
	
/*
=================================================
	CameraPreviewAvailable
=================================================
*/
	NATIVE( void, CameraPreviewAvailable ) (JNI_ARGS, jobject surfaceTexture)
	{
		JAVA_ENV();
		Ptr< AndroidHwCamera >	cam = AJAVAINTERFACE()->GetCamera();

		if ( cam.IsNull() )
			return;

		cam->OnPreviewFrameAvailable( (void *) surfaceTexture );
	}

/*
=================================================
	CameraPicture
=================================================
*/
	NATIVE( void, CameraPicture ) (JNI_ARGS, jbyteArray data, jint width, jint height, jint format)
	{
		JAVA_ENV();
		using namespace Java;

		Ptr< AndroidHwCamera >	cam = AJAVAINTERFACE()->GetCamera();

		if ( cam.IsNull() )
			return;

		JavaArray< jbyte >	jni_data( data, true );

		cam->OnPicture( jni_data.To<uint8>(), ivec2( width, height ), HardwareCamera::ImageFormat::type(format) );
	}
	
/*
=================================================
	CameraZoomChanged
=================================================
*/
	NATIVE( void, CameraZoomChanged ) (JNI_ARGS, jint currentZoom, jint maxZoom,
										jfloat nearFocus, jfloat optimalFocus, jfloat farFocus,
										jfloat focalLength, jfloat vertViewAngle, jfloat horViewAngle)
	{
		JAVA_ENV();
		Ptr< AndroidHwCamera >	cam = AJAVAINTERFACE()->GetCamera();

		if ( cam.IsNull() )
			return;

		cam->OnZoomChanged( currentZoom, maxZoom, nearFocus, optimalFocus, farFocus, focalLength, vertViewAngle, horViewAngle );
	}
	
/*
=================================================
	CameraFaceDetected
=================================================
*/
	NATIVE( void, CameraFaceDetected ) (JNI_ARGS, jint id, jint rectLeft, jint rectBottom, jint rectRight, jint rectTop,
										jint leftEyeX, jint leftEyeY, jint rightEyeX, jint rightEyeY,
										jint mouthX, jint mouthY, jint score)
	{
		JAVA_ENV();
		Ptr< AndroidHwCamera >	cam = AJAVAINTERFACE()->GetCamera();

		if ( cam.IsNull() )
			return;

		cam->OnFaceDetected( id, irect( rectLeft, rectBottom, rectRight, rectTop ), ivec2( leftEyeX, leftEyeY ),
							 ivec2( rightEyeX, rightEyeY ), ivec2( mouthX, mouthY ), score );
	}
	
/*
=================================================
	CameraSetStaticParams
=================================================
*/
	NATIVE( void, CameraSetStaticParams ) (JNI_ARGS, jint minExposure, jint maxExposure, jfloat exposureStep,
										    jintArray pictureFormats, jintArray pictureSizes,
										    jintArray previewFormats, jintArray previewSizes, jintArray previewFpsRanges,
											jintArray antibandings, jintArray colorEffects, jintArray flashModes,
											jintArray focusModes, jintArray sceneModes, jintArray whiteBalances)
	{
		JAVA_ENV();
		Ptr< AndroidHwCamera >	cam = AJAVAINTERFACE()->GetCamera();

		if ( cam.IsNull() )
			return;

		using namespace Java;
		
		JavaArray<jint>	jni_pictureFormats(		pictureFormats,		true );
		JavaArray<jint>	jni_pictureSizes(		pictureSizes,		true );
		JavaArray<jint>	jni_previewFormats(		previewFormats,		true );
		JavaArray<jint>	jni_previewSizes(		previewSizes,		true );
		JavaArray<jint>	jni_previewFpsRanges(	previewFpsRanges,	true );
		JavaArray<jint>	jni_antibandings(		antibandings,		true );
		JavaArray<jint>	jni_colorEffects(		colorEffects,		true );
		JavaArray<jint>	jni_flashModes(			flashModes,			true );
		JavaArray<jint>	jni_focusModes(			focusModes,			true );
		JavaArray<jint>	jni_sceneModes(			sceneModes,			true );
		JavaArray<jint>	jni_whiteBalances(		whiteBalances,		true );
		
		cam->SetStaticParams( minExposure, maxExposure, exposureStep,
			jni_pictureFormats.To<	HardwareCamera::ImageFormat::type	>(),
			jni_pictureSizes.Cast<	ivec2								>(),
			jni_previewFormats.To<	HardwareCamera::ImageFormat::type	>(),
			jni_previewSizes.Cast<	ivec2								>(),
			jni_previewFpsRanges.Cast< ivec2							>(),
			jni_antibandings.To<	HardwareCamera::Antibanding::type	>(),
			jni_colorEffects.To<	HardwareCamera::Effect::type		>(),
			jni_flashModes.To<		HardwareCamera::FlashMode::type		>(),
			jni_focusModes.To<		HardwareCamera::FocusMode::type		>(),
			jni_sceneModes.To<		HardwareCamera::SceneMode::type		>(),
			jni_whiteBalances.To<	HardwareCamera::WhiteBalance::type	>()
		);
	}
	
/*
=================================================
	SendEngineEvent
=================================================
*/
	NATIVE( void, SendEngineEvent ) (JNI_ARGS, jint eventId, jint param0, jint param1, jint param2)
	{
		JAVA_ENV();

		APLATFORM()->OnSendEngineEvent( eventId, param0, param1, param2 );
	}
	
/*
=================================================
	allow
=================================================
*/
	NATIVE( void, allow ) (JNI_ARGS, int policyReason)
	{
		JAVA_ENV();

		enum {
			LICENSED		= 256,
		};
		
		ASSERT( policyReason == LICENSED );
		AJAVAINTERFACE()->OnCheckLicenseResponse( ELicenseCheckResponse::LICENSED, "" );
	}
	
/*
=================================================
	dontAllow
=================================================
*/
	NATIVE( void, dontAllow ) (JNI_ARGS, int policyReason)
	{
		JAVA_ENV();

		enum {
			LICENSED		= 256,
			NOT_LICENSED	= 561,
			RETRY			= 291,
		};

		string						error	= "Dont allow: ";
		ELicenseCheckResponse::type code	= ELicenseCheckResponse::NOT_LICENSED;

		switch ( policyReason )
		{
			case LICENSED :			code = ELicenseCheckResponse::NOT_LICENSED;	error << "licensed";		break;	// ???
			case NOT_LICENSED :		code = ELicenseCheckResponse::NOT_LICENSED;	error << "not licensed";	break;
			default :				code = ELicenseCheckResponse::ERROR;		error << "unknown code " << policyReason;	break;
			case RETRY :
			{
				code = ELicenseCheckResponse::RETRY;
				error.ClearMem();

				if ( not AJAVAINTERFACE()->IsNetworkConnected() )
				{
					code = ELicenseCheckResponse::NO_INTERNET_CONNECTION;
				}
				break;
			}
		}
		
		AJAVAINTERFACE()->OnCheckLicenseResponse( code, error );
	}
	
/*
=================================================
	applicationError
=================================================
*/
	NATIVE( void, applicationError ) (JNI_ARGS, int errorCode)
	{
		JAVA_ENV();

		enum {
			ERROR_INVALID_PACKAGE_NAME	= 1,
			ERROR_NON_MATCHING_UID		= 2,
			ERROR_NOT_MARKET_MANAGED	= 3,
			ERROR_CHECK_IN_PROGRESS		= 4,
			ERROR_INVALID_PUBLIC_KEY	= 5,
			ERROR_MISSING_PERMISSION	= 6,
		};

		string	error = "Error: ";

		switch ( errorCode )
		{
			case ERROR_INVALID_PACKAGE_NAME :	error << "invalid package name";		break;
			case ERROR_NON_MATCHING_UID :		error << "non metching UID";			break;
			case ERROR_NOT_MARKET_MANAGED :		error << "not merket managed";			break;
			case ERROR_CHECK_IN_PROGRESS :		error << "check in progress";			break;
			case ERROR_INVALID_PUBLIC_KEY :		error << "invalid public key";			break;
			case ERROR_MISSING_PERMISSION :		error << "missing permission";			break;
			default :							error << "unknown error " << errorCode;	break;
		}

		AJAVAINTERFACE()->OnCheckLicenseResponse( ELicenseCheckResponse::ERROR, error );
	}
	
#	undef JAVA_ENV
#	undef APLATFORM
#	undef AJAVAINTERFACE

}	// Platforms
}	// Engine

#endif	// PLATFORM_ANDROID